#include "StdAfx.h"
#include ".\preprocessor.h"
#include "tokenizer.h"

CPreprocessor::CPreprocessor(const string &mainfile, const string& path)
:	m_sMainFile(mainfile),
	m_sPath(path)
{
}

CPreprocessor::~CPreprocessor(void)
{
}


void CPreprocessor::ResetProgram()
{
	m_sProgram = "";
	m_vClassesToBuild.clear();
	m_classes.clear();
	m_importedFiles.clear();
}


string& CPreprocessor::Process()
{
	ResetProgram();

	string filepath = m_sPath + m_sMainFile;

#ifdef KEEP_LOG
	Log("Main file %s preprocess\n",m_sMainFile.c_str());
#endif

	FILE* pfile = fopen(filepath.c_str(),"r");
	if(pfile==NULL)
	{
		Log("Error opening %s for preprocessing\n",filepath);
		return m_sProgram;
	}

	//
	// put all the chars of the file into a string
	// but put between spaces the characters { and }
	string data;
	int c;
	while ((c=fgetc(pfile)) != EOF)
	{
		if ((char)c=='}' || (char)c=='{')
		{
			data += " ";
			data += (char)c;
			data += " ";
		}
		else
		{
			data += (char)c;
		}
	}
	fclose(pfile);


	//
	// now create a tokenizer object from those data to find the words inside it
	CTokenizerNoComments tokenizer(data);
	int level=0;
	string token;
	bool bBeforeImport=false;

	while (tokenizer.nextToken())
	{
		token = tokenizer.getToken();

		if(token == "{")
			level++;
		if(token == "}")
			level--;
		
		if(level == 0)
		{

			if(token == "import")
			{
				if ( !tokenizer.nextToken() )
				{
					Log("Error importing file at %s\n",m_sMainFile.c_str());
					m_sProgram="";
					return m_sProgram;
				}

				token = tokenizer.getToken();

				//
				// if file already imported, dont import it again
				if ( m_importedFiles.find(token)!=m_importedFiles.end())
					continue;

				if (!AddToProgramFile(token))
				{
					Log("Import error in file %s",m_sMainFile.c_str());
					m_sProgram="";
					return m_sProgram;
				}

				//
				// add this newly imported file to the already imported ones	
				m_importedFiles[token]=true;
				continue;
			}
			else
			{
				if (token=="main")
				{
					if(!tokenizer.nextToken())
					{
						Log("Error: main() not defined properly in %s\n",m_sMainFile.c_str());
						m_sProgram="";
						return m_sProgram;
					}

					token = tokenizer.getToken();

					if(token!="{")
					{
						Log("Error: main() not defined properly in %s\n",m_sMainFile.c_str());
						m_sProgram="";
						return m_sProgram;
					}

					level++;

					while (tokenizer.nextToken())
					{
						token = tokenizer.getToken();
						
						if (token=="{")
						{
							Log("Error: main() not defined properly in %s,"
								"maybe there is a missing '}'?\n",m_sMainFile.c_str());
							m_sProgram="";
							return m_sProgram;
						}

						if (token=="}")
						{
							--level;
							break;
						}

						string ns="";
						for (int i=0; i<(int)token.length(); i++)
						{
							if(token[i] == ':')
								ns += " ";
							else
								ns += token[i];
						}

						m_vClassesToBuild.push_back(ns);
						m_classes[ns]="";

#ifdef KEEP_LOG
						static int times=1;
						Log("%d: classes = %s\n",times,ns.c_str());
						times++;
#endif
					}

					if(level!=0)
					{
						Log("Error: in main() %s\n",m_sMainFile.c_str());
						m_sProgram="";
						return m_sProgram;
					}
					continue;
				
				}// end of token=="main"
			}//end of not import
		}//end of if level==0

		m_sProgram += " ";
		m_sProgram += token;
		m_sProgram += " ";
	}

	BuildClasses();
	return m_sProgram;
}


string& CPreprocessor::GetProgram()
{
	return m_sProgram;
}


bool CPreprocessor::AddToProgramFile(const string &filepath)
{
	//
	//relative to main file
	string nFilePath = m_sPath + filepath;

#ifdef LOG_PREPROCESSOR
	Log("importing %s\n",nFilePath);
#endif

	FILE* pfile = fopen(nFilePath.c_str(),"r");
	if(pfile==NULL)
	{
		Log("Error reading file %s\n",nFilePath);
		return false;
	}

	//
	//put all the chars of the file into a string
	m_sProgram += "\n\n";
	string data="";
	int c;

	while((c=fgetc(pfile))!=EOF)
	{
		if ((char)c=='}' || (char)c=='{')
		{
			data += " ";
			data += (char)c;
			data += " ";
		}
		else
		{
			data += (char)c;
		}
	}

	CTokenizerNoComments tokenizer(data);
	string token;
	while (tokenizer.nextToken())
	{
		token = tokenizer.getToken();
		m_sProgram += " ";
		m_sProgram += token;
		m_sProgram += " ";
	}
	
	fclose(pfile);
	
#ifdef LOG_PREPROCESSOR
	Log("The program is: \n%s\n\n",m_sProgram);
#endif

	return true;
}




void CPreprocessor::BuildClasses()
{
	vector<string>::iterator sIter,sEnd;
	sIter = m_vClassesToBuild.begin();
	sEnd = m_vClassesToBuild.end();

#ifdef LOG_PREPROCESSOR
	Log("Classes to build:\n");
#endif

	while (sIter!=sEnd)
	{
#ifdef LOG_PREPROCESSOR
		Log("%s\n",*sIter);
#endif


		CTokenizer tokenizer(*sIter);
		vector<string> classesOfThis;
		while (tokenizer.nextToken())
		{
			string classData="";
			string token = tokenizer.getToken();
			ReadDataByLevelAndName(0,token,classData);
			if (classData!="")
			{
				classesOfThis.push_back(classData);
#ifdef LOG_PREPROCESSOR
				Log("class data are %s\n",classData);
#endif
			}
			else
			{
#ifdef LOG_PREPROCESSOR
				Log("class %s not found\n",token);
#endif
			}
		}

		if (classesOfThis.size()!=0)
		{
			m_partClasses[*sIter] = classesOfThis;
		}

		sIter++;
	}
}


void CPreprocessor::ReadDataByLevelAndName(const int thelevel,const string &classname,
										   string &data)
{
	CTokenizer tokenizer(m_sProgram);
	int level=0;
	string token="";
	bool atClassLooking=false;

	while (tokenizer.nextToken())
	{
		token = tokenizer.getToken();
		if(level==thelevel)
		{
			if (token==classname)
			{
				atClassLooking=true;
			}
		}
	


		if (atClassLooking)
		{
			data += " ";
			data += token;
			data += " ";
		}

		if(token=="{")
		{
			level++;
			continue;
		}
		else if(token=="}")
		{
			if (atClassLooking)
			{
				if (level==(thelevel+1))
				{
					return;
				}
			}
			--level;
			continue;
		}
	}
}



bool CPreprocessor::Exists(const string& classname)
{
	if (m_partClasses.find(classname)!=m_partClasses.end())
		return true;
	return false;
}



vector<string>& CPreprocessor::Get(const string& classNameInheritance)
{
	return m_partClasses[classNameInheritance];
}